// implementation for texops.h
//  a set of globally-useful operations on OpenGL textures

#include "texops.h"

#ifdef USE_TTF
extern TTF_Font *systemfont;
#endif
static GLuint last_used_texture=0;

/**
 * Returns the first power of 2 greater or equal to the specified value.
 *
 * @param value that the
 *
 *
 */
int powerOfTwo( int value )
{
	int result = 1 ;
	while ( result < value )
		result *= 2;
	return result ;
}

void reset_texture()
{
	last_used_texture = 0;
}

void use_texture(GLuint tex)
{
	if (tex != last_used_texture)
	{
		glBindTexture(GL_TEXTURE_2D, tex);
		last_used_texture = tex;
	}
}

GLuint build_texture (SDL_Surface *file_surf, GLuint filter)
{
	GLuint tex = 0;
	int w, h;

	SDL_Surface *temp_surf = NULL;

	w = powerOfTwo(file_surf->w);
	h = powerOfTwo(file_surf->h);
	if (file_surf->flags & (SDL_SRCALPHA | SDL_SRCCOLORKEY)) {
		// Loaded file has a transparent color?
		temp_surf = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h, 32 /* bits */,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN // OpenGL RGBA masks
				0x000000FF,
				0x0000FF00,
				0x00FF0000,
				0xFF000000
#else
				0xFF000000,
				0x00FF0000,
				0x0000FF00,
				0x000000FF
#endif
				) ;
		if (temp_surf == NULL) { printf("Error creating 32bit surface for texture: %s\n",SDL_GetError()); return 0; }
		// clear the dest surface
		SDL_FillRect( temp_surf, 0, SDL_MapRGBA(temp_surf->format,0,0,0,0) ) ;
		SDL_SetAlpha(file_surf,0,0);
		SDL_BlitSurface(file_surf,NULL,temp_surf,NULL);

		glGenTextures( 1, &tex );
		glBindTexture(GL_TEXTURE_2D,tex);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ;
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ;

#warning TODO: allow user to supply own GL texture quality, instead of just 4
		glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_surf->pixels);
		SDL_FreeSurface(temp_surf);
	} else {
		// Loaded file does not have transparent areas.
		temp_surf = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h, 24 /* bits */,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN // OpenGL RGBA masks
				0x000000FF,
				0x0000FF00,
				0x00FF0000,
				0x00000000
#else
				0x00FF0000,
				0x0000FF00,
				0x000000FF,
				0x00000000
#endif
				) ;
		if (temp_surf == NULL) { printf("Error creating 24bit surface for texture: %s\n",SDL_GetError()); return 0; }
		SDL_BlitSurface(file_surf,NULL,temp_surf,NULL);
		glGenTextures( 1, &tex );
		glBindTexture(GL_TEXTURE_2D,tex);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ;
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ;
#warning TODO: allow user to supply own GL texture quality, instead of just 3
		glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, temp_surf->pixels);
		SDL_FreeSurface(temp_surf);
	}
	return tex;
}

GLuint load_texture(const char *fname, GLuint filter, unsigned int *w, unsigned int *h)
{
	GLuint tex;
	SDL_Surface *file_surf = load_image(fname);
	if (file_surf == NULL) { printf("Error loading texture: %s\n",IMG_GetError()); return 0; }

	if (w != NULL) *w = powerOfTwo(file_surf->w);
	if (h != NULL) *h = powerOfTwo(file_surf->h);

	tex = build_texture(file_surf,filter);

	SDL_FreeSurface(file_surf);
	return tex;
}

GLuint load_texture_colorkey(const char *fname, GLuint filter,unsigned char r,unsigned char g,unsigned char b, unsigned int *w, unsigned int *h)
{
	GLuint tex;
	SDL_Surface *file_surf = load_image(fname);
	if (file_surf == NULL) { printf("Error loading texture: %s\n",IMG_GetError()); return 0; }
	SDL_Surface *temp_surf = SDL_DisplayFormat(file_surf);
	if (temp_surf == NULL) { printf("Error creating 24bit surface for texture: %s\n",SDL_GetError()); temp_surf = file_surf; }
    else
		SDL_FreeSurface(file_surf);
	
	SDL_SetColorKey(temp_surf,SDL_SRCCOLORKEY,SDL_MapRGB(temp_surf->format,r,g,b));

	// set w / h
	if (w != NULL) *w = powerOfTwo(temp_surf->w);
	if (h != NULL) *h = powerOfTwo(temp_surf->h);
	
	tex = build_texture(temp_surf,filter);
	SDL_FreeSurface(temp_surf);
	return tex;
}

void rebuild_texture (GLuint tex, SDL_Surface *file_surf)
{
	SDL_Surface *temp_surf = NULL;

	glBindTexture(GL_TEXTURE_2D,tex);

	if (file_surf->flags & (SDL_SRCALPHA | SDL_SRCCOLORKEY)) {
		// Loaded file has a transparent color?
		temp_surf = SDL_CreateRGBSurface( SDL_SWSURFACE, file_surf->w, file_surf->h, 32 /* bits */,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN // OpenGL RGBA masks
										 0x000000FF,
										 0x0000FF00,
										 0x00FF0000,
										 0xFF000000
#else
										 0xFF000000,
										 0x00FF0000,
										 0x0000FF00,
										 0x000000FF
#endif
										 ) ;
		if (temp_surf == NULL) { printf("Error creating 32bit surface for texture: %s\n",SDL_GetError()); return; }
		// clear the dest surface
		SDL_FillRect( temp_surf, 0, SDL_MapRGBA(temp_surf->format,0,0,0,0) ) ;
		SDL_SetAlpha(file_surf,0,0);
		SDL_BlitSurface(file_surf,NULL,temp_surf,NULL);

/*		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ;
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ; */

		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, temp_surf->w, temp_surf->h, GL_RGBA, GL_UNSIGNED_BYTE, temp_surf->pixels);
	} else {
		// Loaded file does not have transparent areas.
		temp_surf = SDL_CreateRGBSurface( SDL_SWSURFACE, file_surf->w, file_surf->h, 24 /* bits */,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN // OpenGL RGBA masks
										 0x000000FF,
										 0x0000FF00,
										 0x00FF0000,
										 0x00000000
#else
										 0x00FF0000,
										 0x0000FF00,
										 0x000000FF,
										 0x00000000
#endif
										 ) ;
		if (temp_surf == NULL) { printf("Error creating 24bit surface for texture: %s\n",SDL_GetError()); return; }
		SDL_BlitSurface(file_surf,NULL,temp_surf,NULL);

/*		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ;
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ; */
		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, temp_surf->w, temp_surf->h, GL_RGB, GL_UNSIGNED_BYTE, temp_surf->pixels);
	}
	SDL_FreeSurface(temp_surf);
}

SDL_Surface *tex_from_smkframe(unsigned char *palette, unsigned char *image, int w, int h)
{
    SDL_Color colors[256];
    int i;
    SDL_Surface *temp;

    for (i = 0; i < 256; i ++)
    {
        colors[i].r = palette[i * 3];
        colors[i].g = palette[(i * 3) + 1];
        colors[i].b = palette[(i * 3) + 2];
    }

    temp = SDL_CreateRGBSurfaceFrom (image, w,h,8,w,0,0,0,0);
    if (temp == NULL)
    {
        fprintf(stderr,"ERROR in SDL_CreateRGBSurfaceFrom(image,%d,%d,8,%d,0,0,0,0)\n",w,h,w);
        return NULL;
    }

    if (!SDL_SetColors(temp,colors,0,256))
    {
        fprintf(stderr,"WARNING: SDL_SetColors() returned zero: movie colors may be skewed.\n");
    }

    return temp;
}

void glTC2i(GLuint x, GLuint y, GLuint w, GLuint h)
{
	glTexCoord2f((float)x / w, (float)y / h);
}

void glBox(GLuint x, GLuint y, GLuint w, GLuint h, GLuint t_x, GLuint t_y, GLuint t_w, GLuint t_h)
{
/*	GLuint w = powerOfTwo(x);
	GLuint h = powerOfTwo(y);

    glBindTexture(GL_TEXTURE_2D,tex);*/
	glBegin(GL_QUADS);
	glTC2i(t_x,t_y, t_w,t_h);
	glVertex2i(x, y);
	glTC2i(t_x,t_y + h, t_w,t_h);
	glVertex2i(x, y + h);
	glTC2i(t_x + w,t_y + h, t_w,t_h);
	glVertex2i(x + w, y + h);
	glTC2i(t_x + w,t_y, t_w,t_h);
	glVertex2i(x + w, y);
	glEnd();
}

Uint32 next_frame(Uint32 interval, void *param)
{
    SDL_Event event;
    SDL_UserEvent userevent;

    /* In this example, our callback pushes an SDL_USEREVENT event
	 into the queue, and causes ourself to be called again at the
	 same interval: */

    userevent.type = SDL_USEREVENT;
    userevent.code = 0;
    userevent.data1 = NULL;
    userevent.data2 = NULL;

    event.type = SDL_USEREVENT;
    event.user = userevent;

    SDL_PushEvent(&event);
    return(interval);
}

 
struct font *load_font(const char *defname)
{
	char buffer[5000] = {'\0'};
	char fname[128] = {'\0'};
	char rect[5000] = {'\0'};
	struct font *ret = NULL;

	short start_char = -1, char_count = -1;
	
	FILE *fp = fopen(defname,"r");

	if (fp == NULL) { fprintf(stderr,"Couldn't open %s:\n",defname); perror("Error was:"); return NULL;	}

	while (!feof(fp))
	{
		fgets(buffer,5000,fp);
		if (strcmp(buffer,"StartCharacter") == 0)
		{
			start_char = atoi(strchr(buffer,'=') + 1);
		} else if (strcmp(buffer,"CharacterCount") == 0)
		{
			char_count = atoi(strchr(buffer,'=') + 1);
		} else if (strcmp(buffer,"FontImageFilename") == 0)
		{
			strcpy(fname, strchr(buffer,'=') + 1);
		} else if (strcmp(buffer,"RectChars") == 0)
		{
			strcpy(rect, strchr(buffer,'=') + 1);
		}
	}
	fclose(fp);

	ret = malloc(sizeof(struct font));
	memset(ret,0,sizeof(struct font));

	return ret;
}

void free_font(struct font *font)
{
    if (font == NULL) return;

	int i;
	for (i = 0; i < 256; i ++)
	{
		if (font->r[i] != NULL)
		{
			free(font->r[i]);
		}
	}
	if (font->tex != NULL) SDL_FreeSurface(font->tex);
	free(font);
}

#ifdef USE_TTF
GLuint render_text(const char *text, SDL_Color text_color, GLuint existing_texture)
{
	GLuint tex = existing_texture;
	SDL_Surface *text_surface;
	SDL_Surface *temp_surf = NULL;
	int w, h;

	// step 0: empty message -> empty texture
	if (text == NULL) return 0;

	// step 1: render the text.
	if(!(text_surface=TTF_RenderText_Blended(systemfont,text,text_color))) {
		//handle error here, perhaps print TTF_GetError at least
		printf("TTF_RenderText_Blended(font,%s,color): %s\n", text, TTF_GetError());
		return 0;
	} else {
		w = powerOfTwo(text_surface->w);
		h = powerOfTwo(text_surface->h);

		// create a larger temporary surface in OpenGL-readable RGBA format
		temp_surf = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h, 32 /* bits */,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN // OpenGL RGBA masks
				0x000000FF,
				0x0000FF00,
				0x00FF0000,
				0xFF000000
#else
				0xFF000000,
				0x00FF0000,
				0x0000FF00,
				0x000000FF
#endif
				) ;

		// Throw an error if we failed to create the surface
		if (temp_surf == NULL) { printf("Error creating 32bit surface for text blit of '%s': %s\n",text,SDL_GetError()); free(text_surface); return 0; }

		// clear the dest surface
		SDL_FillRect( temp_surf, 0, SDL_MapRGBA(temp_surf->format,0,0,0,0) ) ;
		SDL_SetAlpha(text_surface,0,0);
		SDL_BlitSurface(text_surface,NULL,temp_surf,NULL);
		SDL_FreeSurface(text_surface);

		if (!tex) glGenTextures( 1, &tex );
		glBindTexture(GL_TEXTURE_2D,tex);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ;
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ;

		glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_surf->pixels);
		SDL_FreeSurface(temp_surf);

		// return the texture
		return tex;
	}
}

#endif
